/**
 * 
 */
package com.googlecode.xcarve.coder.hessian;

import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import com.googlecode.xcarve.coder.Coder;
import com.googlecode.xcarve.io.UnsafeByteArrayInputStream;
import com.googlecode.xcarve.io.UnsafeByteArrayOutputStream;
import com.googlecode.xcarve.object.Header;
import com.googlecode.xcarve.object.NettyMessage;

/**
 * @author xinyun.d
 * 2014年9月8日
 */
public class HessianCoder implements Coder{
	static final Logger logger = LoggerFactory.getLogger(HessianCoder.class);
	static final byte[] DEFAULT_LENGTH = new byte[4];
	@Override
	public void encode(NettyMessage msg,ByteBuf buf) throws Exception {
		UnsafeByteArrayOutputStream byteArray = new UnsafeByteArrayOutputStream();
		Hessian2Output out = new Hessian2Output(byteArray);
		Header header = msg.getHeader();
		buf.writeByte((header.getMagic()));
		buf.writeByte((header.getVersion()));
		buf.writeByte(header.getCodecType());
		buf.writeBytes(DEFAULT_LENGTH);
		int mapLength = 0;
		if(CollectionUtils.isEmpty(header.getAttachment())){
			buf.writeInt(0);
		}else{
			out.writeObject(header.getAttachment());
			out.flush();
			mapLength = byteArray.toByteArray().length;
			buf.writeInt(mapLength);
			buf.writeBytes(byteArray.toByteArray());	
			byteArray.reset();
		}
		Object body = msg.getBody();
		if(body==null){
			buf.writeBytes(DEFAULT_LENGTH);
			logger.info("message body is null.");
			return;
		}
		out.writeObject(body);
		out.flush();
		buf.writeInt(byteArray.toByteArray().length);
		buf.writeBytes(byteArray.toByteArray());	
		buf.setInt(3, buf.readableBytes()-7);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public Object decode(ByteBuf in) throws Exception {
		try {
			NettyMessage msg = new NettyMessage();
			byte magic = in.readByte();
			if(magic!=0x12){
				logger.info("this is illegal message. magic={}",magic);
				return null;
			}
			Header header = new Header();
			header.setMagic(magic);
			header.setVersion(in.readByte());
			header.setCodecType(in.readByte());
			header.setLength(in.readInt());
			int mapbyteLength = in.readInt();
			int readerIndex = in.readerIndex();
			if(mapbyteLength>0){
				byte[] dst = new byte[mapbyteLength];
				in.readBytes(dst, 0, mapbyteLength);
				UnsafeByteArrayInputStream byteInput = new UnsafeByteArrayInputStream(dst);
				Hessian2Input decode = new Hessian2Input(byteInput);
				Map<String,Object> map = (Map<String,Object>)decode.readObject(Map.class);
				header.setAttachment(map);
			}
			msg.setHeader(header);
			if(logger.isDebugEnabled()){
				logger.debug("decode from buffer used by hessian, message header ={}",header);	
			}
			in.readerIndex(readerIndex+mapbyteLength);
			int objectLength = in.readInt();
			if(objectLength<=0){
				logger.info("decode message successfull,but message has no body. ");
				return msg;
			}
			readerIndex = in.readerIndex();
			byte[] dst = new byte[objectLength];
			in.readBytes(dst, 0, objectLength);
			UnsafeByteArrayInputStream byteInput = new UnsafeByteArrayInputStream(dst);
			Hessian2Input decode = new Hessian2Input(byteInput);
			Object obj = decode.readObject();
			msg.setBody(obj);
			if(logger.isDebugEnabled()){
				logger.info("decode message successfull used by hessian. msg={}",msg);
			}
			return msg;
		} catch (Exception e) {
			logger.error("decode error. " , e);
		}finally{
			ReferenceCountUtil.release(in);
		}
		return null;
	}
	
	
	 
}
